/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package familytree.persistencia.util;


import org.hibernate.*;
import org.hibernate.cfg.*;
import org.apache.commons.logging.*;

import javax.naming.*;

/**
 * Clase que sirve de ayuda para el arranque y configuracion de Hibernate
 * 
 * <p>
 * Usa inicializalizadores estáticos para leer opciones de arranque y crear
 * <tt>Configuration</tt> y <tt>SessionFactory</tt>.
 * <p>
 * 
 * This class also tries to figure out if JNDI binding of the <tt>SessionFactory</tt>
 * is used, otherwise it falls back to a global static variable (Singleton). If
 * you use this helper class to obtain a <tt>SessionFactory</tt> in your code,
 * you are shielded from these deployment differences.
 * <p>
 * Otra ventaja de esta clases es el acceso al objeto <tt>Configuration</tt> que fue
 * ysado para construir la actual <tt>SessionFactory</tt>. Con esto puedes acceder 
 * al mapeado mediante programacion e incluso cambiar y reiniciar el <tt>SessionFactory</tt>
 
 * <p>
 * Note: This class supports annotations if you replace the line that creates
 * a Configuration object.
 * <p>
 * Note: This class supports only one data store. Support for several
 * <tt>SessionFactory</tt> instances can be easily added (through a static <tt>Map</tt>,
 * for example). You could then lookup a <tt>SessionFactory</tt> by its name.
 *
 * @author Christian Bauer
 * Modificado por Antonio García Barrachina
 */
public class HibernateUtil {

    private static Log log = LogFactory.getLog(HibernateUtil.class);

    private static Configuration configuration;
    private static SessionFactory sessionFactory;

    static {
        
        //Creamos el SessionFactory desde el archivo de configuración por defecto
        try {
            log.debug("Initializing Hibernate");

            // Lee hibernate.properties, si se encuentra
            configuration = new Configuration();
            // Para usar annotations: configuration = new AnnotationConfiguration();

            //Lee el hiberate.cfg.xml (tiene que estar presente)
            configuration.configure();

            //Crea y almacena
            rebuildSessionFactory(configuration);

            log.debug("Hibernate initialized, call HibernateUtil.getSessionFactory()");
        } catch (Throwable ex) {
            //Capturamos Throwable para no perder NoClassDefFoundError y otras 
            //subclases de error
            
            log.error("Building SessionFactory failed.", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    /**
     * Devuelve la configuracion de Hibernate usada para crear el SessionFactory.
     *
     * @return Configuration
     */
    public static Configuration getConfiguration() {
        return configuration;
    }

    /**
     * Devuelve el SessionFactory desde una variable static o un JNDI lookup
     *
     * @return SessionFactory
     */
    public static SessionFactory getSessionFactory() {
        String sfName = configuration.getProperty(Environment.SESSION_FACTORY_NAME);
       
        if ( sfName != null) {
            log.debug("Looking up SessionFactory in JNDI");
            try {
                return (SessionFactory) new InitialContext().lookup(sfName);
            } catch (NamingException ex) {
                throw new RuntimeException(ex);
            }
            //Si no hay lo de JNDI
        } else if (sessionFactory == null) {
            rebuildSessionFactory();
        }
        return sessionFactory;
    }

    /**
     * Cierra el actual SessionFactory y reinicia todos los recursos.
     * <p>
     * El único metodo que puede ser llamado de HibernateUtil despues
     * de este es rebuildSessionFactory(Configuration).
     */
    public static void shutdown() {
        log.debug("Shutting down Hibernate");
        //cierra la pool de conexiones
        getSessionFactory().close();

        // Limpia la variable estática
        sessionFactory = null;
    }


    /**
     * Crea la <tt>SessionFactory</tt> a partir de configuration
     * <p>
     * Indicar que este método solo debería ser usado con manejadores static de 
     * SessionFactory no con JNDI u otros registros externos.
     * Este método también cierra los atributos sessionFactory si estaban abiertos;
     */
     public static void rebuildSessionFactory() {
        log.debug("Using current Configuration to rebuild SessionFactory");
        rebuildSessionFactory(configuration);
     }

    /**
     * Reinicia el SessionFactory a partir de la configuración de hibernate
     *
     * <p>
     * HibernateUtil does not configure() the given Configuration object,
     * it directly calls buildSessionFactory(). This method also closes
     * the old static variable SessionFactory before, if it is still open.
     *
     * @param cfg
     */
     public static void rebuildSessionFactory(Configuration cfg) {
        log.debug("Rebuilding the SessionFactory from given Configuration");
        if (sessionFactory != null && !sessionFactory.isClosed())
            sessionFactory.close();
        if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
            log.debug("Managing SessionFactory in JNDI");
            cfg.buildSessionFactory();
        } else {//Lo que usamos nosotros
            log.debug("Holding SessionFactory in static variable");
            sessionFactory = cfg.buildSessionFactory();
        }
        configuration = cfg;
     }
     
     
     

}